package com.gitee.feizns.quickstart.web.resolver;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;

/**
 * 抽象的参数绑定解析器
 * <p>
 *     为什么需要这个类？<br/>
 *     在Spring源码中{@link HandlerMethodArgumentResolverComposite#resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory)}
 *     源码中声明对于{@link HandlerMethodArgumentResolver}接口会只选用第一个{@link HandlerMethodArgumentResolver#supportsParameter(MethodParameter)}<br/>
 *     返回true的参数解析器、对于自定义的参数解析，如果希望能够处理Spring原生{@link WebDataBinder}绑定后的数据项进行处理则这里对原生的{@link ServletModelAttributeMethodProcessor}进行了扩展
 * </p>
 * <p>
 *     如果期望在请求参数的实体类中处理则可以使用该类<br/>
 *     推荐：<br/>
 *     对于一般的非javabean类型则可以新建一个{@link HandlerMethodArgumentResolver}<br/>
 *     对于一般的javabean类型则可以继承该类{@link AbstractServletModelAttributeMethodProcessor}对绑定后的数据项进行处理<br/>
 *     对于{@link RequestBody}的数据项可以使用{@link JacksonAnnotationsInside}
 * </p>
 * @see HandlerMethodArgumentResolver
 * @see HandlerMethodArgumentResolverComposite#resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory)
 * @see WebDataBinder
 * @see ServletModelAttributeMethodProcessor
 * @author feizns
 * @since 2023/2/4
 */
public abstract class AbstractServletModelAttributeMethodProcessor extends ServletModelAttributeMethodProcessor {

    /**
     * @see ServletModelAttributeMethodProcessor
     */
    public AbstractServletModelAttributeMethodProcessor() {
        super(true);
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return !BeanUtils.isSimpleProperty(parameter.getParameterType());
    }

    /**
     * Extension point to bind the request to the target object.
     * @param binder the data binder instance to use for the binding
     * @param request the current request
     */
    protected void bindRequestParameters(@NonNull WebDataBinder binder, @NonNull NativeWebRequest request) {
        //调用父类绑定数据
        super.bindRequestParameters(binder, request);
        //解析数据绑定成功之后的对象、将有注解的进行处理
        processTarget(binder.getTarget());
    }

    /**
     * 处理数据绑定对象
     * @param target 数据绑定对象
     */
    protected abstract void processTarget(Object target);

}
